/*
package yz.com.javautil.test;

import com.xinan.dataservice.SpringContextUtil;
import com.xinan.dataservice.statistic.service.IPartitionService;
import com.xinan.distributeCore.tools.BaseTools;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

*/
/**
 * 分区工具类
 *
 * @author dingshuangbo
 * @since 2021-04-14
 *//*

public class PartitionUtils {

    */
/**
     * 拼接分区表（默认 schemaName 为 public）
     *
     * @param sourceTableName   原表
     * @param partitionType     分区策略（day天、week周、month月、season季度、year年）
     * @param partitionCountVal 分区个数
     * @param tmeMin            开始时间字符串 格式 yyyy-MM-dd HH:mm:ss
     * @param tmeMax            结束时间字符串 格式 yyyy-MM-dd HH:mm:ss
     * @return 新表
     *//*

    public static String getNewTableName(String sourceTableName, String partitionType, int partitionCountVal, String tmeMin, String tmeMax) throws ParseException {
        return getNewTableName(sourceTableName, partitionType, partitionCountVal, tmeMin, tmeMax, "public");
    }

    */
/**
     * 拼接分区表
     *
     * @param sourceTableName   原表
     * @param partitionType     分区策略（day天、week周、month月、season季度、year年）
     * @param partitionCountVal 分区个数
     * @param tmeMin            开始时间字符串 格式 yyyy-MM-dd HH:mm:ss
     * @param tmeMax            结束时间字符串 格式 yyyy-MM-dd HH:mm:ss
     * @param schemaName        数据库 schema 的名称
     * @return 新表
     * @throws ParseException 日期格式问题可能导致的日期转换异常
     *//*

    public static String getNewTableName(String sourceTableName, String partitionType, int partitionCountVal, String tmeMin, String tmeMax, String schemaName) throws ParseException {

        SimpleDateFormat sourceSimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date minDate = sourceSimpleDateFormat.parse(tmeMin);
        Date maxDate = sourceSimpleDateFormat.parse(tmeMax);

        // 获取分区个数
        int timeCount = getPartitionCountByType(partitionType, minDate, maxDate);
        if (timeCount > partitionCountVal) {
            // 时间跨越的分区个数超过了分区表查询上限则使用主表查询
            return sourceTableName;
        }

        // 格式化日期参数
        Calendar minCalender = Calendar.getInstance();
        minCalender.setTime(minDate);
        minCalender.set(Calendar.HOUR_OF_DAY, 0);
        minCalender.set(Calendar.MINUTE, 0);
        minCalender.set(Calendar.SECOND, 0);

        Calendar maxCalender = Calendar.getInstance();
        maxCalender.setTime(maxDate);
        maxCalender.set(Calendar.HOUR_OF_DAY, 0);
        maxCalender.set(Calendar.MINUTE, 0);
        maxCalender.set(Calendar.SECOND, 0);

        // 根据分区类型及日期参数获取分区表名称列表
        List<String> partitionList = new ArrayList<>();
        switch (partitionType) {
            case "day":
                partitionList = getPartitionTableByDay(sourceTableName, minCalender, maxCalender);
                break;
            case "week":
                partitionList = getPartitionTableByWeek(sourceTableName, minCalender, maxCalender);
                break;
            case "month":
                partitionList = getPartitionTableByMonth(sourceTableName, minCalender, maxCalender);
                break;
            case "season":
                partitionList = getPartitionTableBySeason(sourceTableName, minCalender, maxCalender);
                break;
            case "year":
                partitionList = getPartitionTableByYear(sourceTableName, minCalender, maxCalender);
                break;
            default:
                break;
        }

        // 多分区数据合并
        String multiPartitionData;
        // 未分区
        if (partitionList.size() == 0) {
            return sourceTableName;
        }
        // 单个或多个分区：验证分区是否存在并处理新表名称
        else {
            // 查询分区是否存在
            IPartitionService iPartitionService = SpringContextUtil.getBean(IPartitionService.class);
            Map<String, Object> params = new HashMap<>(4);
            params.put("source", sourceTableName);
            params.put("partitionTableNames", partitionList);
            params.put("schema", schemaName);
            long partitionCount = (long) iPartitionService.selectOne("statistic.selectPartition", params);
            // 分区表不存在则返回源表
            if (partitionCount == 0) {
                return sourceTableName;
            }
            List<Map> partitionTableNameList = iPartitionService.select("statistic.selectPartitionTableName", params);
            // 单个分区则返回分区名称
            if (partitionCount == 1) {
                return MapUtils.getString(partitionTableNameList.get(0), "partitionTableName");
            }
            // 多个分区表则对分区表进行合并
            // ( SELECT * FROM schemaName.partitionTableName1 UNION ALL SELECT * FROM schemaName.partitionTableName2 ) T
            // 当作一张表传递给随后的查询语句
            else {
                multiPartitionData = partitionTableNameList.stream()
                        .map(partitionTableName -> String.format("SELECT * FROM %S.%S", schemaName, MapUtils.getString(partitionTableName, "partitionTableName")))
                        .collect(Collectors.joining(" UNION ALL ", "( ", " )"));
            }
        }
        return multiPartitionData;
    }

    */
/**
     * 根据分区策略获取开始时间和结束之间跨越的分区个数
     *
     * @param partitionType 分区策略（day天、week周、month月、season季度、year年）
     * @param minDate       开始日期
     * @param maxDate       结束日期
     * @return 分区格式
     *//*

    private static int getPartitionCountByType(String partitionType, Date minDate, Date maxDate) {
        int days = (int) (Math.ceil((maxDate.getTime() - minDate.getTime()) / (double) (1000 * 3600 * 24)));
        int partitionCount = -1;
        switch (partitionType) {
            case "day":
                partitionCount = days;
                break;
            case "week":
                partitionCount = days / 7;
                break;
            case "month":
                partitionCount = days / 30;
                break;
            case "season":
                partitionCount = days / 90;
                break;
            case "year":
                partitionCount = days / 365;
                break;
            default:
                break;
        }
        return partitionCount;
    }

    */
/**
     * 获取按天的分区表名称列表
     *
     * @param sourceTableName 源表
     * @param minCalender     最新日期
     * @param maxCalender     最大日期
     * @return 分区表名称列表
     *//*

    private static List<String> getPartitionTableByDay(String sourceTableName, Calendar minCalender, Calendar maxCalender) {
        List<String> partitionTableNameList = new ArrayList<>();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
        String beginDay = simpleDateFormat.format(minCalender.getTime());
        if (minCalender.getTime().getTime() == maxCalender.getTime().getTime()) {
            // 同一分区
            partitionTableNameList.add(sourceTableName + "_1_prt_p_day_" + beginDay);
        } else {
            // 跨分区查询
            partitionTableNameList.add(sourceTableName + "_1_prt_p_day_" + beginDay);
            while (minCalender.getTime().getTime() < maxCalender.getTime().getTime()) {
                minCalender.add(Calendar.DATE, 1);
                String day = simpleDateFormat.format(minCalender.getTime());
                partitionTableNameList.add(sourceTableName + "_1_prt_p_day_" + day);
            }
        }
        return partitionTableNameList;
    }

    */
/**
     * 获取按周的分区表名称列表
     *
     * @param sourceTableName 源表
     * @param minCalender     最新日期
     * @param maxCalender     最大日期
     * @return 分区表名称列表
     *//*

    private static List<String> getPartitionTableByWeek(String sourceTableName, Calendar minCalender, Calendar maxCalender) {
        List<String> partitionTableNameList = new ArrayList<>();

        // 开始时间和结束时间所在的分区名字
        String minWeekPartitionName = getWeekPartitionNameByDate(minCalender.getTime());
        String maxWeekPartitionName = getWeekPartitionNameByDate(maxCalender.getTime());

        // 同一分区或跨区
        if (StringUtils.equals(minWeekPartitionName, maxWeekPartitionName)) {
            partitionTableNameList.add(sourceTableName + "_1_prt_p_week_" + minWeekPartitionName);
        } else {
            partitionTableNameList.add(sourceTableName + "_1_prt_p_week_" + minWeekPartitionName);
            // 补充全部分区
            while (minCalender.getTime().getTime() < maxCalender.getTime().getTime()) {
                minCalender.add(Calendar.DATE, 1);
                // 下一天所在的周分区名字
                String nextDayWeekPartitionName = getWeekPartitionNameByDate(minCalender.getTime());
                if (!StringUtils.equals(minWeekPartitionName, nextDayWeekPartitionName)) {
                    // 新的分区
                    if (StringUtils.equals(maxWeekPartitionName, nextDayWeekPartitionName)) {
                        // 结束时间所在的分区
                        partitionTableNameList.add(sourceTableName + "_1_prt_p_week_" + maxWeekPartitionName);
                        break;
                    } else {
                        minWeekPartitionName = nextDayWeekPartitionName;
                        partitionTableNameList.add(sourceTableName + "_1_prt_p_week_" + minWeekPartitionName);
                    }
                }
            }
        }
        return partitionTableNameList;
    }

    */
/**
     * 获取按月的分区表名称列表
     *
     * @param sourceTableName 源表
     * @param minCalender     最新日期
     * @param maxCalender     最大日期
     * @return 分区表名称列表
     *//*

    private static List<String> getPartitionTableByMonth(String sourceTableName, Calendar minCalender, Calendar maxCalender) {
        List<String> partitionTableNameList = new ArrayList<>();

        SimpleDateFormat yyyyMmFormat = new SimpleDateFormat("yyyyMM");
        String minMonthPartitionName = yyyyMmFormat.format(minCalender.getTime());
        String maxMonthPartitionName = yyyyMmFormat.format(maxCalender.getTime());

        if (StringUtils.equals(minMonthPartitionName, maxMonthPartitionName)) {
            // 同一分区
            partitionTableNameList.add(sourceTableName + "_1_prt_p_month_" + minMonthPartitionName);
        } else {
            // 跨分区查询
            partitionTableNameList.add(sourceTableName + "_1_prt_p_month_" + minMonthPartitionName);
            while (minCalender.getTime().getTime() < maxCalender.getTime().getTime()) {
                minCalender.add(Calendar.MONTH, 1);
                // 下一月所在的月分区名字
                String nextMonthPartitionName = yyyyMmFormat.format(minCalender.getTime());
                if (!StringUtils.equals(minMonthPartitionName, nextMonthPartitionName)) {
                    // 新的分区
                    if (StringUtils.equals(maxMonthPartitionName, nextMonthPartitionName)) {
                        // 结束时间所在的分区
                        partitionTableNameList.add(sourceTableName + "_1_prt_p_month_" + maxMonthPartitionName);
                        break;
                    } else {
                        minMonthPartitionName = nextMonthPartitionName;
                        partitionTableNameList.add(sourceTableName + "_1_prt_p_month_" + minMonthPartitionName);
                    }
                }
            }
        }
        return partitionTableNameList;
    }

    */
/**
     * 获取按季的分区表名称列表
     *
     * @param sourceTableName 源表
     * @param minCalender     最新日期
     * @param maxCalender     最大日期
     * @return 分区表名称列表
     *//*

    private static List<String> getPartitionTableBySeason(String sourceTableName, Calendar minCalender, Calendar maxCalender) {
        List<String> partitionTableNameList = new ArrayList<>();
        // 开始时间所在的分区名字
        String minSeasonPartitionName = getSeasonPartitionNameByDate(minCalender.getTime());
        // 结束时间所在的分区名字
        String maxSeasonPartitionName = getSeasonPartitionNameByDate(maxCalender.getTime());

        if (StringUtils.equals(minSeasonPartitionName, maxSeasonPartitionName)) {
            // 同一分区
            partitionTableNameList.add(sourceTableName + "_1_prt_p_season_" + minSeasonPartitionName);
        } else {
            // 跨分区查询
            partitionTableNameList.add(sourceTableName + "_1_prt_p_season_" + minSeasonPartitionName);
            while (minCalender.getTime().getTime() < maxCalender.getTime().getTime()) {
                minCalender.add(Calendar.DATE, 80);
                // 下一所在的季度分区名字
                String nextSeasonPartitionName = getSeasonPartitionNameByDate(minCalender.getTime());
                if (!StringUtils.equals(minSeasonPartitionName, nextSeasonPartitionName)) {
                    // 新的分区
                    if (StringUtils.equals(maxSeasonPartitionName, nextSeasonPartitionName)) {
                        // 结束时间所在的分区
                        partitionTableNameList.add(sourceTableName + "_1_prt_p_season_" + maxSeasonPartitionName);
                        break;
                    } else {
                        minSeasonPartitionName = nextSeasonPartitionName;
                        partitionTableNameList.add(sourceTableName + "_1_prt_p_season_" + minSeasonPartitionName);
                    }
                }
            }
        }
        return partitionTableNameList;
    }

    */
/**
     * 获取按年的分区表名称列表
     *
     * @param sourceTableName 源表
     * @param minCalender     最新日期
     * @param maxCalender     最大日期
     * @return 分区表名称列表
     *//*

    private static List<String> getPartitionTableByYear(String sourceTableName, Calendar minCalender, Calendar maxCalender) {
        List<String> partitionTableNameList = new ArrayList<>();

        SimpleDateFormat yyyyFormat = new SimpleDateFormat("yyyy");
        String minYearPartitionName = yyyyFormat.format(minCalender.getTime());
        String maxYearPartitionName = yyyyFormat.format(maxCalender.getTime());
        if (StringUtils.equals(minYearPartitionName, maxYearPartitionName)) {
            // 同一分区
            partitionTableNameList.add(sourceTableName + "_1_prt_p_year_" + minYearPartitionName);
        } else {
            // 跨分区查询
            partitionTableNameList.add(sourceTableName + "_1_prt_p_year_" + minYearPartitionName);
            while (minCalender.getTime().getTime() < maxCalender.getTime().getTime()) {
                minCalender.add(Calendar.YEAR, 1);
                // 下一年所在的年分区名字
                String nextYearPartitionName = yyyyFormat.format(minCalender.getTime());
                if (!StringUtils.equals(minYearPartitionName, nextYearPartitionName)) {
                    // 新的分区
                    if (StringUtils.equals(maxYearPartitionName, nextYearPartitionName)) {
                        // 结束时间所在的分区
                        partitionTableNameList.add(sourceTableName + "_1_prt_p_year_" + maxYearPartitionName);
                        break;
                    } else {
                        minYearPartitionName = nextYearPartitionName;
                        partitionTableNameList.add(sourceTableName + "_1_prt_p_year_" + minYearPartitionName);
                    }

                }

            }
        }
        return partitionTableNameList;
    }

    */
/**
     * 获取时间所在的周分区名字
     *
     * @param date 日期
     * @return 分区名称
     *//*

    private static String getWeekPartitionNameByDate(Date date) {
        // 格式化日期
        SimpleDateFormat yyyyMmDdFormat = new SimpleDateFormat("yyyyMMdd");
        String dateStr = yyyyMmDdFormat.format(date);
        // 截取day
        int day = Integer.parseInt(StringUtils.substring(dateStr, 6, 8));
        String yyyyMm = StringUtils.substring(dateStr, 0, 6);
        // 拼接分区表名称
        if (day < 8) {
            return yyyyMm + "01";
        }
        if (day < 15) {
            return yyyyMm + "02";
        }
        if (day < 22) {
            return yyyyMm + "03";
        }
        if (day < 29) {
            return yyyyMm + "04";
        }
        return yyyyMm + "05";
    }

    */
/**
     * 获取时间所在的季分区名字
     *
     * @param date 日期
     * @return 分区名称
     *//*

    private static String getSeasonPartitionNameByDate(Date date) {
        // 格式化日期
        SimpleDateFormat yyyyMmFormat = new SimpleDateFormat("yyyyMM");
        String dateStr = yyyyMmFormat.format(date);
        // 截取 month
        int month = Integer.parseInt(StringUtils.substring(dateStr, 4, 6));
        String yyyy = StringUtils.substring(dateStr, 0, 4);
        // 拼接分区表名称
        if (month < 4) {
            return yyyy + "01";
        }
        if (month < 7) {
            return yyyy + "02";
        }
        if (month < 10) {
            return yyyy + "03";
        }
        return yyyy + "04";
    }

    */
/**
     * 根据原表、分区策略和时间获取所在分区表名字
     *
     * @param sourceTableName 原表
     * @param partitionType   分区策略（day天、week周、month月、season季度、year年）
     * @param date            时间
     * @return 新表
     *//*

    public static String getPartitionName(String sourceTableName, String partitionType, Date date) {
        String partitionName = "";
        switch (partitionType) {
            case "day":
                partitionName = sourceTableName + "_1_prt_p_day_" + BaseTools.getDateStrByStr(date, "yyyyMMdd");
                break;
            case "week":
                partitionName = sourceTableName + "_1_prt_p_week_" + getWeekPartitionNameByDate(date);
                break;
            case "month":
                partitionName = sourceTableName + "_1_prt_p_month_" + BaseTools.getDateStrByStr(date, "yyyyMM");
                break;
            case "season":
                partitionName = sourceTableName + "_1_prt_p_season_" + getSeasonPartitionNameByDate(date);
                break;
            case "year":
                partitionName = sourceTableName + "_1_prt_p_year_" + BaseTools.getDateStrByStr(date, "yyyy");
                break;
            default:
                break;
        }
        return partitionName;
    }

    */
/**
     * 根据分区策略和时间获取所在分区表名字（不带前缀）
     *
     * @param partitionType 分区策略（day天、week周、month月、season季度、year年）
     * @param date          时间
     * @return 新表
     *//*

    public static String getPartitionName(String partitionType, Date date) {
        String partitionName = "";
        switch (partitionType) {
            case "day":
                partitionName = "p_day_" + BaseTools.getDateStrByStr(date, "yyyyMMdd");
                break;
            case "week":
                partitionName = "p_week_" + getWeekPartitionNameByDate(date);
                break;
            case "month":
                partitionName = "p_month_" + BaseTools.getDateStrByStr(date, "yyyyMM");
                break;
            case "season":
                partitionName = "p_season_" + getSeasonPartitionNameByDate(date);
                break;
            case "year":
                partitionName = "p_year_" + BaseTools.getDateStrByStr(date, "yyyy");
                break;
            default:
                break;
        }
        return partitionName;
    }

    */
/**
     * 根据分区策略和时间获取所在分区表的开始时间和结束时间
     *
     * @param partitionType 分区策略（day天、week周、month月、season季度、year年）
     * @param date          时间
     * @return 新表
     *//*

    public static String[] getPartitionBeginEnd(String partitionType, Date date) {
        String[] partitionBeginEnd = new String[2];
        String dayStr = BaseTools.getDateStrByStr(date, "yyyy-MM-dd");
        int yyyy = Integer.parseInt(StringUtils.substring(dayStr, 0, 4));
        int mm = Integer.parseInt(StringUtils.substring(dayStr, 5, 7));
        int dd = Integer.parseInt(StringUtils.substring(dayStr, 8, 10));
        switch (partitionType) {
            case "day":
                partitionBeginEnd[0] = dayStr + " 00:00:00";
                partitionBeginEnd[1] = dayStr + " 23:59:59";
                break;
            case "week":
                partitionBeginEnd[0] = BaseTools.getFirstWeekDayByDate(date, "yyyy-MM-dd") + " 00:00:00";
                partitionBeginEnd[1] = BaseTools.getLastWeekDayByDate(date, "yyyy-MM-dd") + " 23:59:59";
                break;
            case "month":
                partitionBeginEnd[0] = BaseTools.getFirstMonthDayByDate(date, "yyyy-MM-dd") + " 00:00:00";
                partitionBeginEnd[1] = BaseTools.getLastMonthDayByDate(date, "yyyy-MM-dd") + " 23:59:59";
                break;
            case "season":
                partitionBeginEnd[0] = BaseTools.getFirstSeasonDayByDate(date, "yyyy-MM-dd") + " 00:00:00";
                partitionBeginEnd[1] = BaseTools.getLastSeasonDayByDate(date, "yyyy-MM-dd") + " 23:59:59";
                break;
            case "year":
                partitionBeginEnd[0] = BaseTools.getFirstYearDayByDate(date, "yyyy-MM-dd") + " 00:00:00";
                partitionBeginEnd[1] = BaseTools.getLastYearDayByDate(date, "yyyy-MM-dd") + " 23:59:59";
                break;
            default:
                break;
        }
        return partitionBeginEnd;
    }

    public static void main(String[] args) throws Exception {
        Date date = BaseTools.getDateByStr("2021-02-05 12:00:00", "yyyy-MM-dd HH:mm:ss");
        Calendar lastDayByMonth = Calendar.getInstance();
        lastDayByMonth.setTime(date);
        lastDayByMonth.set(Calendar.DAY_OF_MONTH, lastDayByMonth.getActualMaximum(Calendar.DAY_OF_MONTH));
        String lastDay = BaseTools.getDateStrByStr(lastDayByMonth.getTime(), "yyyy-MM-dd HH:mm:ss");
        System.out.println(lastDay);

    }

}
*/
